﻿using Esponce.Common;
using Esponce.QRCode.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Windows.UI;

namespace Esponce.QRCode
{
	public class QRCodeClient : QRCodeClientBase
	{
		#region /generate
		/// <summary>
		/// Overloaded. Generates a QR Code image.
		/// </summary>
		/// <param name="content">A message to be encoded in QR code.</param>
		/// <param name="format">Output image format.</param>
		/// <param name="moduleSize">QR code module size in pixels.</param>
		/// <param name="padding">QR code version. Values 1-40 or null to be automatically determined.</param>
		/// <param name="version">Padding value in modules.</param>
		/// <param name="em">Encoding mode.</param>
		/// <param name="ec">Error correction level.</param>
		/// <param name="foreground">ARGB color for foreground or null for default.</param>
		/// <param name="background">ARGB color for background or null for default.</param>
		/// <param name="shorten">Name of URL shortener service or null for none.</param>
		/// <returns>Returns QR Code image stream.</returns>
		public async Task<Stream> GenerateAsync(string content, string format = "png", int moduleSize = 8, int padding = 4, short? version = null, QREncodeMode em = QREncodeMode.Default, QRErrorCorrection ec = QRErrorCorrection.Default, Color? foreground = null, Color? background = null, string shorten = null)
		{
			var model = new QRCodeModel();
			model.Content = content;
			model.Format = format;
			model.Size = moduleSize;
			model.Padding = padding;
			model.Version = version;
			model.EncodeMode = em;
			model.ErrorCorrection = ec;
			model.Foreground = foreground;
			model.Background = background;
			model.Shorten = shorten;
			return await GenerateAsync(model);
		}

		/// <summary>
		/// Generates a QR Code image.
		/// </summary>
		/// <param name="model">Parameters for generating a QR Code.</param>
		/// <returns>Returns QR Code image stream.</returns>
		public async Task<Stream> GenerateAsync(QRCodeModel model)
		{
			if (model == null)
			{
				throw new ArgumentNullException("model");
			}

			string url = UriBuilder.BuildGenerateUrl(model, false);
			string mimeType = QRCodeHelper.GetMimeType(model.Format) + "," + base.GetMimeType();
			var stream = Request("GET", url, model, mimeType);
			return stream;
		}
		#endregion

		#region /decode
		/// <summary>
		/// Decodes a QR Code and returns a message.
		/// </summary>
		/// <param name="input">Image stream.</param>
		/// <param name="format">Image format of the stream.</param>
		/// <returns>Returns plain text message.</returns>
		public async Task<string> DecodeAsync(Stream input, string format = "png")
		{
			string url = UriBuilder.BuildDecodeUrl(format);
			var stream = Request("POST", url, input);

			var result = Deserialize<QRCodeModel>(stream);
			string content = result.Content;

			return content;
		}
		#endregion

		#region /track/list
		/// <summary>
		/// Gets a list of campaigns and trackable QR Codes.
		/// </summary>
		/// <returns>Returns list of campaigns and trackable QR Codes.</returns>
		public async Task<TrackListResponse> GetTrackListAsync()
		{
			string url = UriBuilder.BuildTrackListUrl();
			var stream = Request("GET", url);

			var result = Deserialize<TrackListResponse>(stream);
			return result;
		}
		#endregion

		#region /track/campaign
		public async Task<TrackCampaign> GetTrackCampaignAsync(string id)
		{
			if (string.IsNullOrEmpty(id))
			{
				throw new ArgumentException("Campaign id is missing!");
			}

			string url = UriBuilder.BuildTrackCampaignUrl(id);
			var stream = Request("GET", url);

			var result = Deserialize<TrackCampaign>(stream);
			return result;
		}

		public async Task<string> CreateTrackCampaignAsync(TrackCampaign model)
		{
			if (model == null)
			{
				throw new ArgumentNullException("model", "Campaign model is missing!");
			}

			string url = UriBuilder.BuildTrackCampaignUrl();
			var stream = Request("POST", url, model);

			var result = Deserialize<TrackCampaign>(stream);
			return result.Id;
		}

		public async Task UpdateTrackCampaignAsync(TrackCampaign model)
		{
			if (model == null)
			{
				throw new ArgumentNullException("model", "Campaign model is missing!");
			}

			if (model.Id == null)
			{
				throw new ArgumentNullException("model.Id", "Campaign id is missing!");
			}

			string url = UriBuilder.BuildTrackCampaignUrl(model.Id);
			var stream = Request("PUT", url, model);
		}

		public async Task DeleteTrackCampaignAsync(string id)
		{
			if (string.IsNullOrEmpty(id))
			{
				throw new ArgumentException("Campaign id is missing!");
			}

			string url = UriBuilder.BuildTrackCampaignUrl(id);
			var stream = Request("DELETE", url);
		}
		#endregion

		#region /track/qrcode
		public async Task<TrackQRCode> GetTrackQRCodeAsync(string id)
		{
			if (string.IsNullOrEmpty(id))
			{
				throw new ArgumentException("QR Code id is missing!");
			}

			string url = UriBuilder.BuildTrackQRCodeUrl(id);
			var stream = Request("GET", url);

			var result = Deserialize<TrackQRCode>(stream);
			return result;
		}

		public async Task<string> CreateTrackQRCodeAsync(TrackQRCode model)
		{
			if (model == null)
			{
				throw new ArgumentNullException("model", "QR Code model is missing!");
			}

			string url = UriBuilder.BuildTrackQRCodeUrl();
			var stream = Request("POST", url, model);

			var result = Deserialize<TrackQRCode>(stream);
			return result.Id;
		}

		public async Task UpdateTrackQRCodeAsync(TrackQRCode model)
		{
			if (model == null)
			{
				throw new ArgumentNullException("model", "QR Code model is missing!");
			}

			if (model.Id == null)
			{
				throw new ArgumentNullException("model.Id", "QR Code id is missing!");
			}

			string url = UriBuilder.BuildTrackQRCodeUrl(model.Id);
			var stream = Request("PUT", url, model);
		}

		public async Task DeleteTrackQRCodeAsync(string id)
		{
			if (string.IsNullOrEmpty(id))
			{
				throw new ArgumentException("QR Code id is missing!");
			}

			string url = UriBuilder.BuildTrackQRCodeUrl(id);
			var stream = Request("DELETE", url);
		}
		#endregion

		#region /track/statistics
		/// <summary>
		/// Gets statistics for a trackable QR Code.
		/// </summary>
		/// <param name="id">QR Code identifier.</param>
		/// <returns>Returns data defined by the format.</returns>
		public async Task<List<Record>> GetTrackStatisticsAsync(string id)
		{
			using (var stream = await GetTrackStatisticsAsync(id, ExportFormat.JSON))
			{
				using (var reader = new StreamReader(stream))
				{
					string json = reader.ReadToEnd();
					var result = Helper.JsonDeserialize<List<Record>>(json);
					return result;
				}
			}
		}

		/// <summary>
		/// Gets statistics for a trackable QR Code.
		/// </summary>
		/// <param name="id">QR Code identifier.</param>
		/// <param name="format">Export format for statistics.</param>
		/// <returns>Returns data defined by the format.</returns>
		public async Task<Stream> GetTrackStatisticsAsync(string id, ExportFormat format)
		{
			string url = UriBuilder.BuildTrackStatisticsUrl(id, format);
			var stream = Request("GET", url);
			return stream;
		}
		#endregion

		#region /track/export
		/// <summary>
		/// Exports campaigns and trackable QR Codes.
		/// </summary>
		/// <param name="format">Export format.</param>
		/// <returns>Returns data defined by the format.</returns>
		public async Task<List<TrackDataModel>> ExportTrackRecordsAsync()
		{
			using (var stream = await ExportTrackRecordsAsync(ExportFormat.JSON))
			{
				using (var reader = new StreamReader(stream))
				{
					string json = reader.ReadToEnd();
					var result = Helper.JsonDeserialize<List<TrackDataModel>>(json);
					return result;
				}
			}
		}

		/// <summary>
		/// Exports campaigns and trackable QR Codes.
		/// </summary>
		/// <param name="format">Export format.</param>
		/// <returns>Returns data defined by the format.</returns>
		public async Task<Stream> ExportTrackRecordsAsync(ExportFormat format)
		{
			string url = UriBuilder.BuildTrackExportUrl(format);
			var stream = Request("GET", url);
			return stream;
		}
		#endregion

		#region /track/import
		/// <summary>
		/// Imports campaigns and trackable QR Codes.
		/// </summary>
		/// <param name="input">List of items to import.</param>
		/// <returns>Returns data defined by the format.</returns>
		public async Task<List<TrackImportFeedback>> ImportTrackRecordsAsync(IEnumerable<TrackDataModel> list)
		{
			if (list == null)
			{
				throw new ArgumentNullException("list");
			}

			string json = Helper.JsonSerialize(list);
			byte[] buffer = Encoding.UTF8.GetBytes(json);
			using (var stream = new MemoryStream(buffer))
			{
				return await ImportTrackRecordsAsync(stream, ImportFormat.JSON);
			}
		}

		/// <summary>
		/// Imports campaigns and trackable QR Codes.
		/// </summary>
		/// <param name="input">Data to upload.</param>
		/// <param name="format">Format of data to upload.</param>
		/// <returns>Returns data defined by the format.</returns>
		public async Task<List<TrackImportFeedback>> ImportTrackRecordsAsync(Stream input, ImportFormat format)
		{
			string url = UriBuilder.BuildTrackImportUrl(format);
			using (var stream = Request("POST", url, input))
			{
				using (var reader = new StreamReader(stream))
				{
					string json = reader.ReadToEnd();
					var result = Helper.JsonDeserialize<List<TrackImportFeedback>>(json);
					return result;
				}
			}
		}
		#endregion
	}
}
